﻿using Furion.DatabaseAccessor;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.Extensions.Caching.Distributed;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using YFurion.Entities;
using YFurion.Entities.Extensions;
using YFurion.Localization.Extensions;
using YFurion.Locators;

namespace YFurion.Application
{
    /// <summary>
    /// 继承此类即可实现基础方法
    /// 方法包括：CURD、获取全部、分页获取 
    /// </summary>
    /// <typeparam name="TEntity">数据实体类型</typeparam>
    /// <typeparam name="TEntityDto">数据实体对应DTO类型</typeparam>
    /// <typeparam name="TKey">数据实体主键类型</typeparam>
    /// <typeparam name="TGetAllInput">查询实体对应DTO类型</typeparam>
    public abstract class EFCoreApplicationService<TEntity, TEntityDto, TKey, TGetAllInput> : EFCoreApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TEntityDto>, IEFCoreApplicationService<TEntityDto, TKey, TGetAllInput>
        where TEntity : class, IYEntityBase<TKey>, new()
        where TEntityDto : class, IEntityDto<TKey>
    {
    }

    /// <summary>
    /// 继承此类即可实现基础方法
    /// 方法包括：CURD、获取全部、分页获取 
    /// </summary>
    /// <typeparam name="TEntity">数据实体类型</typeparam>
    /// <typeparam name="TEntityDto">数据实体对应DTO类型</typeparam>
    /// <typeparam name="TKey">数据实体主键类型</typeparam>
    /// <typeparam name="TGetAllInput">查询实体对应DTO类型</typeparam>
    /// <typeparam name="TCreateInputDto">创建实体对应DTO类型</typeparam>
    public abstract class EFCoreApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInputDto> : EFCoreApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInputDto, TCreateInputDto>, IEFCoreApplicationService<TEntityDto, TKey, TGetAllInput, TCreateInputDto>
        where TEntity : class, IYEntityBase<TKey>, new()
        where TEntityDto : class, IEntityDto<TKey>
        where TCreateInputDto : class, IEntityDto<TKey>
    {
    }

    /// <summary>
    /// 继承此类即可实现基础方法
    /// 方法包括：CURD、获取全部、分页获取 
    /// </summary>
    /// <typeparam name="TEntity">数据实体类型</typeparam>
    /// <typeparam name="TEntityDto">数据实体对应DTO类型</typeparam>
    /// <typeparam name="TKey">数据实体主键类型</typeparam>
    /// <typeparam name="TGetAllInput">查询实体对应DTO类型</typeparam>
    /// <typeparam name="TCreateInputDto">创建实体对应DTO类型</typeparam>
    /// <typeparam name="TUpdateInputDto">更新实体对应DTO类型</typeparam>
    public abstract class EFCoreApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInputDto, TUpdateInputDto> : MSApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInputDto, TUpdateInputDto, MasterDbContextLocator, SlaveDbContextLocator>, IEFCoreApplicationService<TEntityDto, TKey, TGetAllInput, TCreateInputDto, TUpdateInputDto>
        where TEntity : class, IYEntityBase<TKey>, new()
        where TEntityDto : class, IEntityDto<TKey>
        where TCreateInputDto : class, IEntityDto<TKey>
        where TUpdateInputDto : class, IEntityDto<TKey>
    {
    }

    /// <summary>
    /// 继承此类即可实现基础方法
    /// 方法包括：CURD、获取全部、分页获取 
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TEntityDto"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TGetAllInput"></typeparam>
    /// <typeparam name="TMasterDbContextLocator"></typeparam>
    /// <typeparam name="TSlaveDbContextLocator"></typeparam>
    public abstract class MSApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TMasterDbContextLocator, TSlaveDbContextLocator> : MSApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TEntityDto, TMasterDbContextLocator, TSlaveDbContextLocator>, IMSApplicationService<TEntityDto, TKey, TGetAllInput, TMasterDbContextLocator, TSlaveDbContextLocator>
        where TEntity : class, IYEntityBase<TKey>, new()
        where TEntityDto : class, IEntityDto<TKey>
        where TMasterDbContextLocator : class, IDbContextLocator
        where TSlaveDbContextLocator : class, IDbContextLocator
    {
    }

    /// <summary>
    /// 继承此类即可实现基础方法
    /// 方法包括：CURD、获取全部、分页获取 
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TEntityDto"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TGetAllInput"></typeparam>
    /// <typeparam name="TCreateInputDto"></typeparam>
    /// <typeparam name="TMasterDbContextLocator"></typeparam>
    /// <typeparam name="TSlaveDbContextLocator"></typeparam>
    public abstract class MSApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInputDto, TMasterDbContextLocator, TSlaveDbContextLocator> : MSApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInputDto, TCreateInputDto, TMasterDbContextLocator, TSlaveDbContextLocator>, IMSApplicationService<TEntityDto, TKey, TGetAllInput, TCreateInputDto, TMasterDbContextLocator, TSlaveDbContextLocator>
        where TEntity : class, IYEntityBase<TKey>, new()
        where TEntityDto : class, IEntityDto<TKey>
        where TCreateInputDto : class, IEntityDto<TKey>
        where TMasterDbContextLocator : class, IDbContextLocator
        where TSlaveDbContextLocator : class, IDbContextLocator
    {
    }

    /// <summary>
    /// 继承此类即可实现基础方法
    /// 方法包括：CURD、获取全部、分页获取 
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TEntityDto"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TGetAllInput"></typeparam>
    /// <typeparam name="TCreateInputDto"></typeparam>
    /// <typeparam name="TUpdateInputDto"></typeparam>
    /// <typeparam name="TMasterDbContextLocator"></typeparam>
    /// <typeparam name="TSlaveDbContextLocator"></typeparam>
    public abstract class MSApplicationService<TEntity, TEntityDto, TKey, TGetAllInput, TCreateInputDto, TUpdateInputDto, TMasterDbContextLocator, TSlaveDbContextLocator> : EFCoreApplicationServiceBase<TEntity, TEntityDto, TKey, TGetAllInput, TMasterDbContextLocator, TSlaveDbContextLocator>, IMSApplicationService<TEntityDto, TKey, TGetAllInput, TCreateInputDto, TUpdateInputDto, TMasterDbContextLocator, TSlaveDbContextLocator>
    where TEntity : class, IYEntityBase<TKey>, new()
    where TEntityDto : class, IEntityDto<TKey>
    where TCreateInputDto : class, IEntityDto<TKey>
    where TUpdateInputDto : class, IEntityDto<TKey>
    where TMasterDbContextLocator : class, IDbContextLocator
    where TSlaveDbContextLocator : class, IDbContextLocator
    {
        /// <summary>
        /// 根据id查询一条
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<TEntityDto> Get(TKey id, CancellationToken cancellationToken = default)
        {
            TEntity entity = await GetEntity(id, cancellationToken);
            return entity.Adapt<TEntityDto>();
        }

        /// <summary>
        /// 根据id查询一条数据缓存
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<TEntityDto> GetCacheById(TKey id, CancellationToken cancellationToken = default)
        {
            TEntity entity = await GetOrCreateCacheAsync(new TEntity { Id = id }.ToString(), async cancellationToken =>
            {
                TEntity entity = await MasterRepository.FindOrDefaultAsync(id, cancellationToken);
                return entity;
            }, cancellationToken, DistributedCache.GetDefaultDistributedCacheEntryOptions());
            TEntityDto entityDto = entity == null ? default(TEntityDto) : entity.Adapt<TEntityDto>();
            return entityDto;
        }

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<PagedResultDto<TEntityDto>> GetAll(TGetAllInput input, CancellationToken cancellationToken = default)
        {
            IQueryable<TEntity> queryable = CreateFilteredQuery(input);

            int totalCount = await queryable.CountAsync(cancellationToken);

            queryable = ApplySorting(queryable, input);
            queryable = ApplyPaging(queryable, input);

            List<TEntity> items = await queryable.ToListAsync(cancellationToken);
            PagedResultDto<TEntityDto> result = new PagedResultDto<TEntityDto>
            {
                Items = items.Adapt<List<TEntityDto>>(),
                TotalCount = totalCount,
            };
            return result;
        }

        /// <summary>
        /// 新增一条
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<TEntityDto> Post(TCreateInputDto input, CancellationToken cancellationToken = default)
        {
            TEntity entity = input.Adapt<TEntity>();
            EntityEntry<TEntity> newEntity = await MasterRepository.InsertNowAsync(entity, cancellationToken: cancellationToken);
            return newEntity.Entity.Adapt<TEntityDto>();
        }

        /// <summary>
        /// 更新一条
        /// </summary>
        /// <param name="input"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task<TEntityDto> Put(TUpdateInputDto input, CancellationToken cancellationToken = default)
        {
            TEntity entity = await MasterRepository.FindOrDefaultAsync(input.Id, cancellationToken);
            await OnUpdateExecuting(entity, cancellationToken);
            TEntityDto entityDto = await UpdateAsync(input, entity, cancellationToken);
            return entityDto;
        }

        /// <summary>
        /// 删除一条
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public virtual async Task Delete(TKey id, CancellationToken cancellationToken = default)
        {
            TEntity entity = await MasterRepository.FindOrDefaultAsync(id, cancellationToken);
            await OnDeleteExecuting(entity, cancellationToken);
            await DeleteAsync(entity, cancellationToken);
        }

        #region 受保护及私有方法
        #region 查询相关
        /// <summary>
        /// 查询一条实体
        /// </summary>
        /// <param name="id"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected async Task<TEntity> GetEntity(TKey id, CancellationToken cancellationToken)
        {
            TEntity entity = await MasterRepository.FindOrDefaultAsync(id, cancellationToken);
            await OnGetExecuting(entity, cancellationToken);
            return entity;
        }
        #endregion

        #region 修改相关
        /// <summary>
        /// 更新实体
        /// </summary>
        /// <param name="input"></param>
        /// <param name="entity"></param>
        /// <param name=""></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected async Task<TEntityDto> UpdateAsync(TUpdateInputDto input, TEntity entity, CancellationToken cancellationToken)
        {
            entity = input.Adapt(entity);
            return await UpdateAsync(entity, cancellationToken);
        }
        /// <summary>
        /// 更新实体
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected async Task<TEntityDto> UpdateAsync(TEntity entity, CancellationToken cancellationToken)
        {
            EntityEntry<TEntity> updateEntity = await MasterRepository.UpdateNowAsync(entity, cancellationToken: cancellationToken);
            return updateEntity.Entity.Adapt<TEntityDto>();
        }
        #endregion

        #region 删除相关
        /// <summary>
        /// 删除实体
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        protected async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken)
        {
            if (entity.IsSoftDeleteObj())
            {
                await SoftDeleteAsync(entity, cancellationToken);
            }
            else
            {
                await MasterRepository.DeleteNowAsync(entity, cancellationToken);
            }
        }
        /// <summary>
        /// 软删除实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        protected async Task<EntityEntry<TEntity>> SoftDeleteAsync(TEntity entity, CancellationToken cancellationToken)
        {
            if (!entity.IsSoftDeleteObj())
            {
                throw Oh(LocalizationManager.GetCurrentString("没有发现属性标记为软删除"));
            }

            entity.SetDeletionProperties(YSession.SubjectId);
            EntityEntry<TEntity> entry = await MasterRepository.UpdateNowAsync(entity, cancellationToken: cancellationToken);
            return entry;
        }
        #endregion
        #endregion
    }
}
